package com.sky.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.*;
import com.sky.entity.*;
import com.sky.exception.OrderBusinessException;
import com.sky.mapper.*;
import com.sky.properties.BaiDuProperties;
import com.sky.result.PageResult;
import com.sky.service.OrderService;
import com.sky.utils.HttpClientUtil;
import com.sky.utils.WeChatPayUtil;
import com.sky.vo.*;
import io.swagger.models.auth.In;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.sky.server.WebSocketServer;


import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private AddressBookMapper addressBookMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WeChatPayUtil weChatPayUtil;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private BaiDuProperties baiDuProperties;
    @Autowired
    private WebSocketServer webSocketServer;


    /**
     * @param ordersSubmitDTO: return OrderSubmitVO
     * @author fenglai
     * @date 2023/12/4 21:06
     * @description 用户下单功能
     */

    @Transactional(rollbackFor = Exception.class)
    @Override
    public OrderSubmitVO submit(OrdersSubmitDTO ordersSubmitDTO) {
        //前面不知道其实前端已经发了总金额过来了 导致显示错误 这里重新做一遍

        //第1步 获得userId(在校验令牌时传入到线程的全局变量中)
        Long userId = BaseContext.getCurrentId();

        //第2步 创建订单对象
        Orders orders = new Orders();

        //第3步 拷贝DTO对象
        BeanUtils.copyProperties(ordersSubmitDTO, orders);

        //第4步 补全DTO缺少的订单信息 这里是设置订单的用户id
        orders.setUserId(userId);

        //第5步 根据用户id 得到用户的购物车
        List<ShoppingCart> shoppingCarts = shoppingCartMapper.selectShoppingCart(ShoppingCart.builder().userId(userId).build());

        //第6步 判断购物车是否存在商品 如果不存在则抛出异常
        if (shoppingCarts == null) {
            throw new OrderBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
        }

        //第7步 根据订单的地址id拿到地址
        AddressBook addressBook = addressBookMapper.selectAddressBookById(orders.getAddressBookId());

        //第8步 自定义订单号 这里使用的是当前时间拼接用户的id
        orders.setNumber("" + LocalDateTime.now() + orders.getUserId());

        //第9步 设置订单的状态 这里是待付款(因为是下单操作)
        orders.setStatus(Orders.PENDING_PAYMENT);

        //接下来都是构造订单对象
        //下单时间就是当前时间
        orders.setOrderTime(LocalDateTime.now());

        //设置订单的地址通过地址簿对象获得省市区进行拼接并且将详细地址拼接在一起
        orders.setAddress(addressBook.getProvinceName() + addressBook.getCityName() + addressBook.getDistrictName() + addressBook.getDetail());

        //设置手机号 也是通过地址簿获得
        orders.setPhone(addressBook.getPhone());

        //设置收货人 也是通过地址簿获得
        orders.setConsignee(addressBook.getConsignee());

        //设置配送状态为未开始配送
        orders.setPayStatus(Orders.UN_PAID);

        //通过用户id获得用户对象
        User user = userMapper.selectUserById(userId);

        //通过用户对象获得用户的名字
        orders.setUserName(user.getName());

        //设置完所有可以获得并且需要的信息后将订单对象插入到数据库中
        orderMapper.insertOrders(orders);

        //创建一个订单详情表 用来存储订单的详情(与购物车信息差不多 缺一个订单id)
        List<OrderDetail> orderDetails = new ArrayList<>();

        //循环遍历购物车(前面做了非空判断 这里不用担心空指针)
        for (ShoppingCart shoppingCart : shoppingCarts) {

            //创建订单详情对象
            OrderDetail orderDetail = new OrderDetail();

            //将购物车对象数据拷贝到订单详情对象中
            BeanUtils.copyProperties(shoppingCart, orderDetail);

            //通过order获得订单对象的id(在插入时需要设置获得id返回值 并且要先获得在设置)
            orderDetail.setOrderId(orders.getId());

            //将完善的订单详情对象存入到集合当中
            orderDetails.add(orderDetail);

            //下单成功则删除购物车商品
            shoppingCartMapper.deleteShoppingCart(shoppingCart);
        }
        //通过订单详情的Mapper将数据插入到数据库当中
        orderDetailMapper.insertOrderDetail(orderDetails);

        Map<String, String> map = new HashMap();
        map.put("address", baiDuProperties.getAddress());
        map.put("output", baiDuProperties.getOutput());
        map.put("ak", baiDuProperties.getAk());

        String response = HttpClientUtil.doGet(baiDuProperties.getUrl(), map);
        Object result = JSONObject.parseObject(response).get("result");
        String location = JSONObject.toJSONString(result);
        location = JSONObject.toJSONString(JSON.parseObject(location).get("location"));
        String lng = JSONObject.toJSONString(JSON.parseObject(location).get("lng"));
        String lat = JSONObject.toJSONString(JSON.parseObject(location).get("lat"));
        String origin = lat + "," + lng;
        //----------------送餐地址---------------------
        map.put("address", orders.getAddress());
        map.put("output", baiDuProperties.getOutput());
        map.put("ak", baiDuProperties.getAk());

        response = HttpClientUtil.doGet(baiDuProperties.getUrl(), map);
        result = JSONObject.parseObject(response).get("result");
        location = JSONObject.toJSONString(result);
        location = JSONObject.toJSONString(JSON.parseObject(location).get("location"));
        lng = JSONObject.toJSONString(JSON.parseObject(location).get("lng"));
        lat = JSONObject.toJSONString(JSON.parseObject(location).get("lat"));

        String destination = lat + "," + lng;

        map.put("origin", origin);
        map.put("destination", destination);
        String responseResult = HttpClientUtil.doGet(baiDuProperties.getCountUrl(), map);
        String result1 = JSONObject.toJSONString(JSON.parseObject(responseResult).get("result"));
        String routes = JSONObject.toJSONString(JSON.parseObject(result1).get("routes"));
        routes = JSONObject.toJSONString(JSON.parseArray(routes).get(0));
        String distances = JSONObject.toJSONString(JSON.parseObject(routes).get("distance"));
        int distance = Integer.parseInt(distances);
        if (distance > BaiDuProperties.maxDestination) {
            throw new OrderBusinessException(MessageConstant.DELIVERY_DISTANCE_EXCEEDS_THE_RANGE);
        }

        //构建VO对象 将所需要的数据传入(之前不知道将菜品的金额存入导致数据不一致)
        OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder().id(orders.getId()).
                orderNumber(orders.getNumber()).orderTime(orders.getOrderTime()).orderAmount(orders.getAmount()).build();

        //最后返回VO对象
        return orderSubmitVO;
    }


    /**
     * 订单支付
     *
     * @param ordersPaymentDTO
     * @return
     */


    public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
        // 当前登录用户id
//        Long userId = BaseContext.getCurrentId();
//        User user = userMapper.selectUserById(userId);
//
//        //调用微信支付接口，生成预支付交易单
//        JSONObject jsonObject = weChatPayUtil.pay(
//                ordersPaymentDTO.getOrderNumber(), //商户订单号
//                new BigDecimal(0.01), //支付金额，单位 元
//                "苍穹外卖订单", //商品描述
//                user.getOpenid() //微信用户的openid
//        );
//
//
//        if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
//            throw new OrderBusinessException("该订单已支付");
//        }
//
//        OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
//        vo.setPackageStr(jsonObject.getString("package"));

        //这里是修改订单的功能 模拟付款成功后调用写好的方法
        paySuccess(ordersPaymentDTO.getOrderNumber());

        //返回只需要显示成功就可以了 这里用空对象
        OrderPaymentVO vo = new OrderPaymentVO();

        return vo;
    }

    /**
     * 支付成功，修改订单状态
     *
     * @param outTradeNo
     */
    public void paySuccess(String outTradeNo) {
        // 当前登录用户id
        Long userId = BaseContext.getCurrentId();

        // 根据订单号查询当前用户的订单
        Orders ordersDB = orderMapper.getByNumberAndUserId(outTradeNo, userId);

        // 根据订单id更新订单的状态、支付方式、支付状态、结账时间
        Orders orders = Orders.builder()
                .id(ordersDB.getId())
                .status(Orders.TO_BE_CONFIRMED)
                .payStatus(Orders.PAID)
                .checkoutTime(LocalDateTime.now())
                .build();

        orderMapper.update(orders);

        Map map = new HashMap();
        map.put("type", 1);
        map.put("orderId", orders.getId());
        map.put("content", "您有新的外卖订单订单号为:" + ordersDB.getNumber() + " 下单时间为:" + ordersDB.getOrderTime());

        webSocketServer.sendToAllClient(JSONObject.toJSONString(map));
    }


    /**
     * @param id: return Orders
     * @author fenglai
     * @date 2023/12/4 21:05
     * @description 查询订单功能
     */
    @Override
    public OrderVO selectOrder(Long id) {
        Orders orders = orderMapper.selectOrder(id);

        List<OrderDetail> detail = orderDetailMapper.getDetail(id);

        OrderVO orderVO = new OrderVO();

        BeanUtils.copyProperties(orders, orderVO);

        orderVO.setOrderDetailList(detail);

        return orderVO;
    }

    /**
     * @param ordersPageQueryDTO: return PageResult
     * @author fenglai
     * @date 2023/12/5 15:35
     * @description 条件查询订单功能
     */
    @Override
    public PageResult pageQueryOrders(OrdersPageQueryDTO ordersPageQueryDTO) {

        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        List<OrderVO> orders = orderMapper.getOrders(ordersPageQueryDTO);
        List<OrderDetail> orderDetails = null;
        for (OrderVO order : orders) {
            orderDetails = orderDetailMapper.getDetail(order.getId());
            if (orderDetails != null) {
                String orderDishes = null;
                for (OrderDetail orderDetail : orderDetails) {
                    orderDishes = orderDetail.getName() + "*" + orderDetail.getNumber() + ";";
                }
                order.setOrderDishes(orderDishes);
            }
        }
        Page<OrderVO> p = (Page<OrderVO>) orders;

        return new PageResult(p.getTotal(), p.getResult());
    }

    /**
     * @param id: return OrderVO
     * @author fenglai
     * @date 2023/12/4 21:05
     * @description 查询订单详情功能
     */
    @Override
    public OrderVO selectOrderDetail(Long id) {

        //创建VO对象
        OrderVO orderVO = new OrderVO();

        //通过id查询orders对象
        Orders orders = orderMapper.selectOrder(id);

        //根据id查询订单详情
        List<OrderDetail> orderDetails = orderDetailMapper.getDetail(id);

        //将订单对象拷贝到VO对象中
        BeanUtils.copyProperties(orders, orderVO);

        //构建空字符串orderDishes
        String orderDishes = "";

        for (OrderDetail orderDetail : orderDetails) {

            //循环遍历订单详情 将菜品名和数量进行拼接
            orderDishes = orderDetail.getName() + "*" + orderDetail.getNumber() + ";";

        }

        //将拼接好的orderDishes存入
        orderVO.setOrderDishes(orderDishes);

        //将订单详情集合存入
        orderVO.setOrderDetailList(orderDetails);

        //返回VO对象
        return orderVO;
    }

    /**
     * @param : return OrderStatisticsVO
     * @author fenglai
     * @date 2023/12/5 20:20
     * @description 统计订单状态功能
     */

    @Override
    public OrderStatisticsVO countOrderStatusNumber() {

        return orderMapper.countOrderStatusNumber();

    }

    /**
     * @param ordersCancelDTO: return void
     * @author fenglai
     * @date 2023/12/4 21:04
     * @description 取消订单功能
     */

    @Override
    public void updateOrder(OrdersCancelDTO ordersCancelDTO) {

        Orders order = new Orders();

        BeanUtils.copyProperties(ordersCancelDTO, order);

        Orders orders = orderMapper.selectOrder(ordersCancelDTO.getId());

        if (orders.getStatus() == Orders.COMPLETED) {

            order.setPayStatus(Orders.REFUND);

        }

        order.setStatus(Orders.CANCELLED);

        order.setCancelTime(LocalDateTime.now());

        orderMapper.update(order);
    }

    /**
     * @param id: return void
     * @author fenglai
     * @date 2023/12/4 21:04
     * @description 派送功能
     */
    @Override
    public void deliveryOrder(Long id) {

        Orders orders = orderMapper.selectOrder(id);

        if (orders.getPayStatus() == Orders.PAID && orders.getStatus() == Orders.CONFIRMED) {

            orders.setStatus(Orders.DELIVERY_IN_PROGRESS);

            orders.setDeliveryStatus(1);

            orderMapper.update(orders);
        } else {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
    }

    /**
     * @param confirmDTO: return void
     * @author fenglai
     * @date 2023/12/4 21:04
     * @description 接单功能
     */
    @Override
    public void comfirmOrder(OrdersConfirmDTO confirmDTO) {

        Orders orders = new Orders();

        BeanUtils.copyProperties(confirmDTO, orders);

        Orders order = orderMapper.selectOrder(orders.getId());

        if (order.getStatus() == Orders.PENDING_PAYMENT && order.getPayStatus() == Orders.UN_PAID) {
            throw new OrderBusinessException(MessageConstant.THE_ORDER_CANNOT_BE_RECEIVED_WITHOUT_PAYMENT);
        }

        orders.setStatus(Orders.CONFIRMED);


        orderMapper.update(orders);
    }

    /**
     * @param ordersRejectionDTO: return void
     * @author fenglai
     * @date 2023/12/4 21:04
     * @description 拒单功能
     */
    @Override
    public void rejection(OrdersRejectionDTO ordersRejectionDTO) {


        Orders order = new Orders();

        BeanUtils.copyProperties(ordersRejectionDTO, order);

        Orders orders = orderMapper.selectOrder(ordersRejectionDTO.getId());

        if (orders.getStatus() == Orders.COMPLETED) {

            order.setPayStatus(Orders.REFUND);

        }

        order.setStatus(Orders.CANCELLED);

        order.setCancelTime(LocalDateTime.now());

        orderMapper.update(order);

    }

    /**
     * @param id: return void
     * @author fenglai
     * @date 2023/12/4 21:03
     * @description 完成订单功能
     */

    @Override
    public void completeOrder(Long id) {

        Orders orders = orderMapper.selectOrder(id);

        orders.setStatus(Orders.COMPLETED);

        orderMapper.update(orders);

    }

    /**
     * @param ordersPageQueryDTO: return PageResult
     * @author fenglai
     * @date 2023/12/4 21:21
     * @description 历史订单查询功能
     */
    @Override
    public PageResult historyOrders(OrdersPageQueryDTO ordersPageQueryDTO) {

        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());

        List<OrderVO> orders = orderMapper.getOrders(ordersPageQueryDTO);

        for (OrderVO order : orders) {
            List<OrderDetail> detail = orderDetailMapper.getDetail(order.getId());
            order.setOrderDetailList(detail);
        }

        Page<OrderVO> ordersVO = (Page<OrderVO>) orders;

        return new PageResult(ordersVO.getTotal(), ordersVO.getResult());

    }

    /**
     * @param id: return void
     * @author fenglai
     * @date 2023/12/4 21:32
     * @description 再来一单的功能
     */
    @Override
    public void repetitionOrder(Long id) {

        Orders orders = orderMapper.selectOrder(id);

        orders.setStatus(Orders.PENDING_PAYMENT);

        orders.setPayStatus(Orders.UN_PAID);

        List<OrderDetail> detail = orderDetailMapper.getDetail(id);
        for (OrderDetail orderDetail : detail) {
            ShoppingCart shoppingCart = new ShoppingCart();
            BeanUtils.copyProperties(orderDetail, shoppingCart);
            shoppingCart.setUserId(BaseContext.getCurrentId());
            shoppingCartMapper.insertShoppingCart(shoppingCart);
        }

    }


    /**
     * @param id: return void
     * @author fenglai
     * @date 2023/12/5 20:19
     * @description 用户取消订单
     */
    @Override
    public void cancelOrder(Long id) {

        Orders orders = orderMapper.selectOrder(id);
        orders.setStatus(Orders.CANCELLED);
        orders.setCancelTime(LocalDateTime.now());
        orders.setPayStatus(Orders.REFUND);
        orderMapper.update(orders);

    }

    /**
     * @param id: return void
     * @author fenglai
     * @date 2023/12/6 9:30
     * @description 用户催单
     */
    @Override
    public void reminderOrder(Long id) {

        Orders orders = orderMapper.selectOrder(id);

        Map map = new HashMap();
        map.put("type", 2);
        map.put("orderId", orders.getId());
        map.put("content", "有用户催单啦 用户单号为:" + orders.getNumber() + " 下单时间为:" + orders.getOrderTime() + "请尽快处理！");

        webSocketServer.sendToAllClient(JSONObject.toJSONString(map));

    }

    public List<LocalDate> getDateList(LocalDate begin, LocalDate end) {
        List<LocalDate> Date = new ArrayList<>();
        Date.add(begin);
        //这里如果不先添加一个进去 会少一次 因为需要包尾
        while (!begin.equals(end)) {
            begin = begin.plusDays(1);
            Date.add(begin);
        }
        return Date;
    }

    @Override
    public OrderReportVO countOrders(OrdersStatisticsDTO dto) {
        //获得时间的区间集合
        List<LocalDate> dateList = getDateList(dto.getBegin(), dto.getEnd());
        //创建总订单数集合
        List<Integer> ordersCountList = new ArrayList<>();
        //创建有效订单数结合
        List<Integer> validOrdersCountList = new ArrayList<>();
        //统计总订单数
        Integer totalOrderCountSum = 0;
        //订单完成率
        Double orderCompletionRate = 0.0;
        //这个是有效订单的总数
        Integer validOrdersCountSum = 0;
        for (LocalDate date : dateList) {
            LocalDateTime begin = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime end = LocalDateTime.of(date, LocalTime.MAX);
            //当天的订单数
            Integer ordersCount = orderMapper.countOrders(null, begin, end);
            //添加到集合中 过会进行字符串拼接
            ordersCountList.add(ordersCount);
            //计算订单总数 这里直接累加
//            totalOrderCountSum += ordersCount;
            //获得当天有效订单数
            Integer validOrdersCount = orderMapper.countOrders(Orders.COMPLETED, begin, end);
            //存入集合 需要进行字符串拼接
            validOrdersCountList.add(validOrdersCount);
            //计算有效订单总数 这里也是累加
//            validOrdersCountSum += validOrdersCount;
        }

        //通过流进行累加 这里面使用了 reduce的sum方法进行累加 其实还可以使用方法引用 这里用的是lambdab表达式
        //totalOrderCountSum = ordersCountList.stream().reduce(0, (a, b) -> a + b);
        //validOrdersCountSum = validOrdersCountList.stream().reduce(0, (a, b) -> a + b);
        //方法引用:
        totalOrderCountSum = ordersCountList.stream().reduce(0, Integer::sum);
        validOrdersCountSum = validOrdersCountList.stream().reduce(0, Integer::sum);

        if (totalOrderCountSum != 0) {
            orderCompletionRate = 1.0 * validOrdersCountSum / totalOrderCountSum;
        }
        return OrderReportVO.builder().
                orderCountList(StringUtils.join(ordersCountList, ",")).
                totalOrderCount(totalOrderCountSum).validOrderCount(validOrdersCountSum).
                validOrderCountList(StringUtils.join(validOrdersCountList, ",")).
                dateList(StringUtils.join(dateList, ",")).
                orderCompletionRate(orderCompletionRate).build();
    }

    @Override
    public UserReportVO userStatistics(OrdersStatisticsDTO dto) {
        //获得时间区间集合
        List<LocalDate> dateList = getDateList(dto.getBegin(), dto.getEnd());
        //创建记录用户总数集合
        List<Integer> totailUserList = new ArrayList<>();
        //创建记录新用户数集合
        List<Integer> newUserList = new ArrayList<>();
        //循环遍历日期区间
        for (LocalDate date : dateList) {
            //创建开始时间 通过of方法 拼接localDateTime 后面拼接时分秒的最小值也就是00:00:00
            LocalDateTime begin = LocalDateTime.of(date, LocalTime.MIN);
            //创建结束时间 通过of方法 拼接localDateTime 后面拼接时分秒的最大值也就是23:59:59999(无限接近24时的值)
            LocalDateTime end = LocalDateTime.of(date, LocalTime.MAX);
            //传入结束时间 用动态sql 来判断是新用户还是用户的总数
            //比如 end 表示 当前天数的最大时间值
            Integer totailUser = userMapper.countUserStatistics(null, end);
            Integer newUser = userMapper.countUserStatistics(begin, end);
            totailUserList.add(totailUser);
            newUserList.add(newUser);
        }
        return UserReportVO.builder().
                newUserList(StringUtils.join(newUserList, ",")).
                dateList(StringUtils.join(dateList, ",")).
                totalUserList(StringUtils.join(totailUserList, ",")).
                build();

    }

    @Override
    public TurnoverReportVO turnoverStatistics(OrdersStatisticsDTO dto) {
        List<LocalDate> dateList = getDateList(dto.getBegin(), dto.getEnd());
        List<Integer> turnoverList = new ArrayList<>();
        for (LocalDate date : dateList) {
            //创建开始时间 通过of方法 拼接localDateTime 后面拼接时分秒的最小值也就是00:00:00
            LocalDateTime begin = LocalDateTime.of(date, LocalTime.MIN);
            //创建结束时间 通过of方法 拼接localDateTime 后面拼接时分秒的最大值也就是23:59:59999(无限接近24时的值)
            LocalDateTime end = LocalDateTime.of(date, LocalTime.MAX);
            Integer Amount = orderMapper.countOrdersAmount(Orders.COMPLETED, begin, end);
            Amount = Amount == null ? 0 : Amount;
            turnoverList.add(Amount);
        }
        return TurnoverReportVO.builder().
                turnoverList(StringUtils.join(turnoverList, ",")).
                dateList(StringUtils.join(dateList, ",")).
                build();

    }

    @Override
    public SalesTop10ReportVO getTop10(OrdersStatisticsDTO ordersStatisticsDTO) {

        List<String> nameList = new ArrayList<>();
        List<Integer> numberList = new ArrayList<>();
            LocalDateTime begin = LocalDateTime.of(ordersStatisticsDTO.getBegin(), LocalTime.MIN);
            LocalDateTime end = LocalDateTime.of(ordersStatisticsDTO.getEnd(), LocalTime.MAX);

            List<GoodsSalesDTO> goodsSalesDTO = orderMapper.selectTop10(Orders.COMPLETED, begin, end);
            for (GoodsSalesDTO salesDTO : goodsSalesDTO) {
                numberList.add(salesDTO.getNumber());
                nameList.add(salesDTO.getName());
            }

        return SalesTop10ReportVO.builder().nameList(StringUtils.join(nameList, ",")).
                numberList(StringUtils.join(numberList, ",")).
                build();

    }


}